﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Interactivity;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows;
using System.Collections;
using System.Windows.Data;
using System.Diagnostics;

namespace XWord.WPF.Behaviors
{
    public class OrderingItemsBehavior : Behavior<ListBox>
    {
        internal class OperationCommand : ICommand
        {
            #region Fields

            readonly Action<object> _execute;
            readonly Predicate<object> _canExecute;

            #endregion // Fields

            #region Constructors

            /// <summary>
            /// Creates a new command that can always execute.
            /// </summary>
            /// <param name="execute">The execution logic.</param>
            public OperationCommand(Action<object> execute)
                : this(execute, null)
            {
            }

            /// <summary>
            /// Creates a new command.
            /// </summary>
            /// <param name="execute">The execution logic.</param>
            /// <param name="canExecute">The execution status logic.</param>
            public OperationCommand(Action<object> execute, Predicate<object> canExecute)
            {
                if (execute == null)
                    throw new ArgumentNullException("execute");

                _execute = execute;
                _canExecute = canExecute;
            }

            #endregion // Constructors

            #region ICommand Members

            [DebuggerStepThrough]
            public bool CanExecute(object parameter)
            {
                return _canExecute == null ? true : _canExecute(parameter);
            }

            public event EventHandler CanExecuteChanged
            {
                add { CommandManager.RequerySuggested += value; }
                remove { CommandManager.RequerySuggested -= value; }
            }

            public void Execute(object parameter)
            {
                _execute(parameter);
            }

            #endregion // ICommand Members
        }

        protected override void OnAttached()
        {
            RegisterCommand();
            if (!isReady)
            {
                MoveTopButton.SetBinding(Button.CommandProperty, new Binding("TopCommand") { Source = this });
                MoveUpButton.SetBinding(Button.CommandProperty, new Binding("UpCommand") { Source = this });
                MoveDownButton.SetBinding(Button.CommandProperty, new Binding("DownCommand") { Source = this });
                MoveToLastButton.SetBinding(Button.CommandProperty, new Binding("BottomCommand") { Source = this });
                this.AssociatedObject.Unloaded += new RoutedEventHandler(OrderingItemsButtons_Unloaded);
                isReady = true;
            }
            base.OnAttached();
        }

        private bool isReady;

        #region Command

        public ICommand UpCommand
        {
            get { return (ICommand)GetValue(UpCommandProperty); }
            set { SetValue(UpCommandProperty, value); }
        }

        public static readonly DependencyProperty UpCommandProperty =
            DependencyProperty.Register("UpCommand", typeof(ICommand), typeof(OrderingItemsBehavior),
            new UIPropertyMetadata(null));

        public ICommand DownCommand
        {
            get { return (ICommand)GetValue(DownCommandProperty); }
            set { SetValue(DownCommandProperty, value); }
        }

        public static readonly DependencyProperty DownCommandProperty =
            DependencyProperty.Register("DownCommand", typeof(ICommand), typeof(OrderingItemsBehavior),
            new UIPropertyMetadata(null));

        public ICommand BottomCommand
        {
            get { return (ICommand)GetValue(BottomCommandProperty); }
            set { SetValue(BottomCommandProperty, value); }
        }

        public static readonly DependencyProperty BottomCommandProperty =
            DependencyProperty.Register("BottomCommand", typeof(ICommand), typeof(OrderingItemsBehavior),
            new UIPropertyMetadata(null));


        public ICommand TopCommand
        {
            get { return (ICommand)GetValue(TopCommandProperty); }
            set { SetValue(TopCommandProperty, value); }
        }

        public static readonly DependencyProperty TopCommandProperty =
            DependencyProperty.Register("TopCommand", typeof(ICommand), typeof(OrderingItemsBehavior),
            new UIPropertyMetadata(null));

        #endregion

        private void RegisterCommand()
        {
            UpCommand = new OperationCommand(param => Up(), param => CanMoveup());
            DownCommand = new OperationCommand(param => Down(), param => CanMovedown());
            BottomCommand = new OperationCommand(param => Bottom(), param => CanMovedown());
            TopCommand = new OperationCommand(param => Top(), param => CanMoveup());
        }

        public virtual bool SelectedItemCanMove()
        {
            if (AssociatedObject == null || AssociatedObject.Items.Count <= 1)
                return false;
            return true;

        }

        private object SelectedItem
        {
            get
            {
                return AssociatedObject.SelectedItem;
            }
        }

        private IList _list
        {
            get
            {
                if (AssociatedObject.ItemsSource != null)
                    return AssociatedObject.ItemsSource as IList;

                return AssociatedObject.Items;
            }
        }

        public virtual bool CanMoveup()
        {
            return SelectedItemCanMove() && !ReferenceEquals(SelectedItem, _list[0]);
        }

        public virtual bool CanMovedown()
        {
            return SelectedItemCanMove() && !ReferenceEquals(SelectedItem, _list[_list.Count - 1]);
        }


        public virtual void Top()
        {
            Move(AssociatedObject.SelectedIndex, 0);
        }

        public virtual void Bottom()
        {
            Move(AssociatedObject.SelectedIndex, AssociatedObject.Items.Count - 1);
        }

        public virtual void Up()
        {
            Move(AssociatedObject.SelectedIndex, AssociatedObject.SelectedIndex - 1);
        }

        public virtual void Down()
        {
            Move(AssociatedObject.SelectedIndex, AssociatedObject.SelectedIndex + 1);
        }


        private void Move(int oldIndex, int newIndex)
        {
            if (oldIndex == -1 || newIndex == -1 || newIndex >= AssociatedObject.Items.Count || oldIndex == newIndex)
                return;

            var methodInfo = AssociatedObject.ItemsSource.GetType().GetMethod("Move");
            if (methodInfo == null) return;
            methodInfo.Invoke(AssociatedObject.ItemsSource, new object[] { oldIndex, newIndex });
        }

        #region button

        public Button MoveTopButton
        {
            get { return (Button)GetValue(MoveTopButtonProperty); }
            set { SetValue(MoveTopButtonProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MoveTopButton.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MoveTopButtonProperty =
            DependencyProperty.Register("MoveTopButton", typeof(Button), typeof(OrderingItemsBehavior), 
            new UIPropertyMetadata(null));



        public Button MoveUpButton
        {
            get { return (Button)GetValue(MoveUpButtonProperty); }
            set { SetValue(MoveUpButtonProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MoveUpButton.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MoveUpButtonProperty =
            DependencyProperty.Register("MoveUpButton", typeof(Button), typeof(OrderingItemsBehavior),
            new UIPropertyMetadata(null));




        public Button MoveDownButton
        {
            get { return (Button)GetValue(MoveDownButtonProperty); }
            set { SetValue(MoveDownButtonProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MoveDownButton.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MoveDownButtonProperty =
            DependencyProperty.Register("MoveDownButton", typeof(Button), typeof(OrderingItemsBehavior),
            new UIPropertyMetadata(null));




        public Button MoveToLastButton
        {
            get { return (Button)GetValue(MoveToLastButtonProperty); }
            set { SetValue(MoveToLastButtonProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MoveToLastButton.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MoveToLastButtonProperty =
            DependencyProperty.Register("MoveToLastButton", typeof(Button), typeof(OrderingItemsBehavior), 
            new UIPropertyMetadata(null));

        #endregion

        void OrderingItemsButtons_Unloaded(object sender, RoutedEventArgs e)
        {
            this.Dispose();
            this.AssociatedObject.Unloaded -= OrderingItemsButtons_Unloaded;
        }

        #region IDisposable Members

        public void Dispose()
        {

            BindingOperations.ClearBinding(MoveTopButton, Button.CommandProperty);
            BindingOperations.ClearBinding(MoveUpButton, Button.CommandProperty);
            BindingOperations.ClearBinding(MoveDownButton, Button.CommandProperty);
            BindingOperations.ClearBinding(MoveToLastButton, Button.CommandProperty);
            TopCommand = null;
            UpCommand = null;
            DownCommand = null;
            BottomCommand = null;
            GC.SuppressFinalize(this);
            isReady = false;
        }

        #endregion
    }
}
